local type,tostring,select,pairs,ipairs,assert=type,tostring,select,pairs,ipairs,assert
local string_len,string_rep,string_match,string_format,string_find,string_byte=
	  string.len,string.rep,string.match,string.format,string.find,string.byte
local table_insert,table_remove=
	  table.insert,table.remove
	  
module(..., package.seeall)

require'zdc.sandbox'
require'zdc.nodeapi'
require'zdc.fieldutil'
require'zdc.idutil'
require'zdc.doc'
require'zdc.deflate'

local dfskey=zdc.fieldutil.dfskey
local dfskey_show=zdc.fieldutil.dfskey_show
local dfskey_autotype=zdc.fieldutil.dfskey_autotype
local conf_do = zdc.sandbox.conf_do
local fast_do = zdc.sandbox.fast_do
local STR=zdc.typeex.STR
local apiset=zdc.nodeapi.set
local apicallact=zdc.nodeapi.callact
local loadactfun=zdc.nodeapi.loadactfun
local get_doc=zdc.doc.get_doc
local isroot=zdc.idutil.IsRoot
local GetRelID=zdc.idutil.GetRelID
local deflate_node_cp=zdc.deflate.deflate_node_cp
--define activattor for node active
local Activators = {}
Activators.lua = function(value, dpl, node, fname)
	local r1,r2=conf_do(value,dpl._sb,dpl._log)
	if not r1 then error('ERROR:['..node.__id..']['..fname..']:'..tostring(r2)) end
	return r1,r2
end

Activators.list = function(value, dpl)
	local nodes = {}
	for line in (value or ""):gmatch("[^%s]+") do
		table_insert(nodes, line)
	end
	return nodes
end

Activators.kv = function(value, dpl)
	local nodes = {}
	for line in string.gmatch(value or "","[^%s]+") do
		local k,v=string_match(line, "^(.+)=(.-)$")
		if k and v then
			nodes[k]=v
		else
			node[line]=''
		end
	end
	return nodes
end

local function inheritance_rules_concat(proto_value, own_value)
	local buf = ""
	if proto_value and string_len(proto_value) > 0 then
		buf = proto_value
	end
	if own_value and string_len(own_value) > 0 then
		if string_len(buf) > 0 then
			buf = buf.."\n"..own_value
		else
			buf = own_value
		end
	end
	return buf
end
local inheritance_rules = {}

function inheritance_rules.abstract(proto_value, own_value,dpl,n,fname)
	local rv=n.__raw_v._abs
	if rv then
		local absar=rawget(n,'__abs')
		if not absar then
			absar={}
			local dal=dpl.dal
			for s in string.gmatch(rv,'[^;]+') do
				local an,msg=dal:GetNode(s)
				if not an then
					error('ERROR:['..node.__id..']['..fname..'] can not get abstract node:'..tostring(s))
				end
				table_insert(absar,an)
			end
			rawset(n,'__abs',absar)
		end
		local av=own_value or ''
		for k=1,#absar do
			local an=absar[k]
			local hv=an:__get_inh_field(fname)
			if hv and #hv>0 then
				av=hv..'\n'..av
			end
		end
		return inheritance_rules_concat(proto_value, av)
	else
		return inheritance_rules_concat(proto_value, own_value)
	end
end

inheritance_rules.concat=inheritance_rules_concat

function inheritance_rules.fallback(proto_value, own_value)
	if own_value ~= nil and own_value~='' then
		return own_value
	else
		return proto_value
	end
end

inheritance_rules.default = inheritance_rules.fallback

Node = {}
local Node_mt = {
	__index = function(t,key)
		local i = Node[key]
		if i~=nil then return i end
		
		i=t.__act_v
		if i then
			i=i[key]
			if i~=nil then return i end
		end

		i=t.__inh_v
		if i then
			i=i[key]
			if i~=nil then return i end
		end

		i=t.__raw_v[key]
		if i~=nil then return i end

		if key=='_fds' then return t:__get_fds() end
		if key=='_dfs' then return t:__get_dfs() end
		i=t:__get_field_define(key)
		if i then
			local dyn=i.dyn
			if dyn then
				if type(dyn)~='function' then
					dyn=assert(loadactfun(t.__dpl.dal,t,dyn),'can not load act '..tostring(dyn))
				end
				return dyn(nil,t.__dpl.dal,t,key)
			end
			--if i and i.proto~=false then
			if i.proto then
				return t.__pto_v[key]
			end
			if t.__raw_v._ins then
				return t.__pto_v[key]
			end
			return i and i.v
		end
	end,
	__newindex = function(t,key,nv)
		local dal=t.__dpl.dal
		assert(apiset(nil,dal,t,key,nv))
	end,
	__call = function(t,act,...)
		local dal=t.__dpl.dal
		-- internal call not check acl
		return select(2,assert(apicallact(nil,dal,t,act,...)))
	end,
	__div = function(n,childkey)
		return n:__get_child(childkey)
	end,
}


function new(args)
	local node = setmetatable({__id=args.id,__dpl=args.dpl,__needsave=args.firstcreate or false,__ik=args.ik or 0,__ts=args.ts or 0,
		__pto_v=false,__raw_v=true,__inh_v=false,__act_v=false}, Node_mt)
	local rv,err
	if type(args.data)=='table' then
		rv=deflate_node_cp(args.data)
		--rv=args.data
	else
		rv,err=conf_do(args.data,node.__dpl._sb,node.__dpl._log)
		if not rv then return rv,err and err.err end
	end
	
	rawset(node,'__org',node)
	node.__raw_v = rv
	node:__do_inh_act()
	return node
end

function Node:__sbenv()
	return self.__dpl._sb
end

function Node:__set_ik(ik)
	assert(ik~=nil)
	self.__ik=ik
end

function Node:__set_ts(ts)
	assert(ts~=nil)
	rawset(self,'__ts_',ts)
end

function Node:__get_inh_field(fn,fd)
	fd=fd or self:__get_field_define(fn)
	if fd then
		if fd.proto then
			return (self.__inh_v and self.__inh_v[fn]) or (self.__pto_v.__get_inh_field and self.__pto_v:__get_inh_field(fn)) or self.__raw_v[fn]
		elseif self.__raw_v._ins then
			return (self.__inh_v and self.__inh_v[fn]) or self.__raw_v[fn] or (self.__pto_v.__get_inh_field and self.__pto_v:__get_inh_field(fn)) or self.__raw_v[fn]
		elseif fd.dyn then
			local dyn=fd.dyn
			if type(dyn)~='function' then
				dyn=assert(loadactfun(self.__dpl.dal,self,dyn),'can not load act '..tostring(dyn))
			end
			return dyn(nil,dal,t,key)
		else
			local v=self.__raw_v[fn]
			if v~=nil then return v end
			return fd.v
		end
	else
		return self.__raw_v[fn]
	end
end

function Node:__get_pto_field(fn,fd)
	fd=fd or self:__get_field_define(fn)
	if fd and fd.proto then
		return self.__pto_v[fn]
	elseif self.__raw_v._ins then
		return self.__pto_v[fn]
	else
		return fd and fd.v
	end
end

function Node:__get_fds()
	local t,i=self,self.__act_v
	while i~=nil do
		i=i and i._fds
		if i then return i end
		t=t.__pto_v
		i=t.__act_v
	end
	return
end
function Node:__get_dfs()
	local t,i=self,self.__act_v
	while i~=nil do
		i=i and i._dfs
		if i then return i end
		t=t.__pto_v
		i=t.__act_v
	end
	return
end

function Node:__get_field_define(key)
	return self._fds[key] or dfskey(self._dfs,key)
end
function Node:__get_field_define_autotype(key)
	local fd=self._fds[key]
	if not fd then
		fd,key=dfskey_autotype(self._dfs,key)
	end
	return fd,key
end

function Node:__get_field_define_show(key)
	local fd,sk=self._fds[key],key
	if not fd then
		fd,sk=dfskey_show(self._dfs,key)
	end
	return fd,sk
end
function Node:__get_act_define(act)
	local t=self._act
	return t and t[act]
end
function Node:__get_acts()
	local ordered_fields = {}
	if not self._act then return ordered_fields end
	for k,v in pairs(self._act) do
		ordered_fields[#ordered_fields+1]=k
	end
	return ordered_fields
end
function Node:__get_child_define(key)
	if type(key)~='string' then return nil,'EBAT1:'..'key='..type(key) .. ' for node id' end
	local _cds=self._cds
	if not _cds then return end
	local f,t=_cds[key]
	if type(f)=='table' then
	 	return f[1],f
	end
	-- deal with dynamic fields
	if f == nil then
		local va = _cds._array
		if va and basetype.U32:_validU(key,va[1]) then
			f=va[2]
			t=va
		end
	end
	if f == nil then	
		if _cds._patterns then
			for i, pattern in ipairs(_cds._patterns) do
				local pt = pattern[1]
				local tp = type(pt)
				if tp == 'string' then
					if string_match(key,pt) then
						local lmt=pattern[2];
						if lmt then
							if basetype.TXT:_validU(key,lmt) then
								f = pattern[3]
								t=pattern
								break;
							end
						else
							f=pattern[3]
							t=pattern
							break
						end
					end
				elseif tp == 'table' then
					if pt:_validU(key,pattern[2]) then
						f =  pattern[3]
						t=pattern
						break
					end
				end
			end
		end
	else
		t=t or {}
	end
	return f,t
end

function Node:__get_childiddef_noexist(ne)
	ne=ne or {}
	local f=self._cds
	if not f then return ne end
	for k,v in pairs(f) do
		if string_byte(k)==95 then
			--skip
		else
			if not self:__get_child(k) then
				ne[#ne+1]=k
			end
		end 
	end
	return ne
end

function Node:__is_come_from(pto)
	local dplptoid=self.__dpl._pto_id
	local selfid=self.__id
	if pto==dplptoid then return true end
	if selfid == pto then return true end
	if selfid==dplptoid then return false end
	local _pto=self.__pto_v
	if _pto then
		if _pto.__id==pto then
			return true
		else
			return _pto:__is_come_from(pto)
		end
	end
end

function Node:__is_proto_val(key)
	local i
	if self.__act_v then 
		i=self.__act_v[key]
		if i~=nil then return false,i end
	end
	if self.__inh_v then
		i=self.__inh_v[key]
		if i~=nil then return false,i end
	end
	i=self.__raw_v[key]
	if i~=nil then return false,i end
	return true,self:__get_pto_field(key)
end

function Node:__get_flag_val(k,nopto)
	local p,i=self:__is_proto_val(k)
	if nopto then
		if p then return '',nil end
		return '',self[k]
	else
		if p then return '@',i end
		return '', self[k]
	end
end

function Node:__set_raw(k,v)
	self.__raw_v[k] = v
end

function Node:__set_field(fds,k,v)
	fds=fds or self:__get_field_define(k)
	self.__raw_v[k] = v
	if fds then
		if (fds[1] or 1) < 1 then
			self:__do_inh_act()
		elseif fds.proto or fds.activate then
			self:__do_inh_act_kv(fds,k,v)
		end
	end
end

function Node:__update_fields(tab)
	local needact
	for k,v in pairs(tab) do
		self.__raw_v[k] = v
		if not needact then
			local fds=self:__get_field_define(k)
			if fds then needact=fds.proto or fds.activate  or ((fds[1] or 1) < 1) end
		end
	end
	if needact then self:__do_inh_act() end
end

function Node:__reset_all()
	local pto=self.__raw_v._pto
	self.__raw_v = {_pto=pto}
	self:__do_inh_act()
end
function Node:__get_fields(tb,lvl1,lvl2,m)
	tb=tb or {}
	lvl1=lvl1 or 1
	local fds=self._fds
	for k,v in pairs(fds) do
		local l1=v[1]
		if l1 and ((l1>=lvl1) and (not lvl2 or l1<lvl2)) then
			if not m or string_match(k,m) then tb[k]=v end
		end
	end
	for k,_ in pairs(self.__raw_v) do
		if not tb[k] and not fds[k] then
			if not m or string_match(k,m) then
				local fd=dfskey(self._dfs,k)
				if fd then
					local l1=fd[1]
					if l1 and ((l1>=lvl1) and (not lvl2 or l1<lvl2)) then
						tb[k]=fd
					end
				end
			end
		end
	end
	return tb
end
function Node:__get_fields_values(tb,lvl1,lvl2,m)
	tb=tb or {}
	lvl1=lvl1 or 1
	local fds=self._fds
	for k,v in pairs(fds) do
		local l1=v[1]
		if l1 and ((l1>=lvl1) and (not lvl2 or l1<lvl2)) then
			if not m or string_match(k,m) then tb[k]=self[k] end
		end
	end
	for k,_ in pairs(self.__raw_v) do
		if not fds[k] then
			if not m or string_match(k,m) then
				local fd=dfskey(self._dfs,k)
				if fd then
					local l1=fd[1]
					if l1 and ((l1>=lvl1) and (not lvl2 or l1<lvl2)) then
						tb[k]=self[k]
					end
				end
			end
		end
	end
	return tb
end

function Node:__get_ordered_fields(lst)
	local of,fdt,er={},{},{}
	for k,v in pairs(lst) do
		local fds=self:__get_field_define(k)
		if fds then
			fdt[k]=fds
			table_insert(of,k)
		else
			table_insert(er,k)
		end
	end
	table.sort(of, function(a,b) return (fdt[a][1] or 0) < (fdt[b][1] or 0) end)
	return of,fdt,er
end

function Node:__get_ordered_fields_name()
	local of = {}
	local __self_fds=self._fds
	for k,v in pairs(__self_fds) do
		table_insert(of,k)
	end
	table.sort(of, function(a,b) return (__self_fds[a][1] or 0) < (__self_fds[b][1] or 0) end)
	return of
end

function Node:__get_ordered_fields_name_lvl(lvl1,lvl2)
	local of = {}
	lvl1 = lvl1 or 1
	lvl2 = lvl2 or lvl1+100
	local __self_fds=self._fds
	for k,v in pairs(__self_fds) do
		if v[1] and v[1] >= lvl1 and v[1] < lvl2 then
			table_insert(of,k)
		end
	end
	table.sort(of, function(a,b) return (__self_fds[a][1] or 0) < (__self_fds[b][1] or 0) end)
	return of
end

--not include  parent's dy-rawvalues
function Node:__get_other_fields(of)
	of = of or {}
	local _f = self._fds
	if self.__raw_v then
		for k,_ in pairs(self.__raw_v) do
			if not _f[k] then 
				table_insert(of,k)
			end
		end
	end
	return of
end

function Node:__check_nil_fields(deep,helpall)
	local nilf=nil
	for field_name, field in pairs(self._fds) do
		if string_byte(field_name)~=95 then
			local ff=self[field_name]
			if ff==nil then
				nilf=nilf or {}
				nilf[#nilf+1]=field_name
				if not helpall then return nilf end 
			end
		end
	end
	if deep then
		for _i,_j in pairs(self:__get_child_all()) do
			local _t=_j:__check_nil_fields(deep,helpall)
			if _t then
				nilf=nilf or {}
				nilf[#nilf+1]=_t
				if not helpall then return nilf end 
			end
		end
	end
	return nilf
end

local function _mc_sub_data(dal,cd,mc,cont)
	if cd._mc[mc] then
		return cont:gsub('~mc~',cd._mc[mc])
	elseif cd._cf then
		local pto=dal:GetNode(mc)
		if pto then
			for _,v in ipairs(cd._cf) do
				if pto:__is_come_from(v) then
					return cont:gsub('~mc~',mc)
				end
			end
		end
	end
end

--TODO: mc auto get from _cfd
function Node:__make_child(id,mc)
	local _cd = self._cds
	if not _cd then return end
	--[[ enter make child , so the node id must not exist
	if self.__dpl.dal:HasNode(self.__id.."/"..id) then
		return nil
	end
	--]]
	local _ct,_dal=_cd[id],self.__dpl.dal
	if _ct then
		local tmplrep
		if type(_ct)=='table' then
			tmplrep=_ct.tmpl
			if not _ct._mc then _ct=_ct[1] end
		end
		if type(_ct) == 'string' then
			if tmplrep then
				_ct=cosmo.fill(_ct,{par=self,id=id,mc=mc})
			end
			return _dal:MakeNode(self.__id.."/"..id,_ct,nil,nil,true)
		else
			local vd = _mc_sub_data(_dal,_ct,mc or '',_ct[1])
			if vd then
				if tmplrep then
					vd=cosmo.fill(vd,{par=self,id=id,mc=mc})
				end
				local r1,r2=_dal:MakeNode(self.__id.."/"..id,vd,nil,nil,true)
				if r1 and mc~='' then r1.__raw_v._cmc=mc end
				return r1,r2
			end
		end
	elseif _cd._patterns then
		for i, pattern in ipairs(_cd._patterns) do
			local pt = pattern[1]
			local tp = type(pt)
			local vvv
			if tp == 'string' then
				if string_match(id,pt) then
					local lmt=pattern[2]
					if (not lmt) or (lmt and STR:_validU(id,lmt)) then
						vvv=pattern[3]
					end
				end
			elseif tp == 'table' then
				local r1,r2 = pt:_validU(id,pattern[2])
				if r1 then
					vvv=pattern[3]
				end
			end
			if vvv then
				if pattern._mc then
					vvv=_mc_sub_data(_dal,pattern,mc or '',pattern[3])
				end
				if vvv then
					if pattern.tmpl then
						vvv=cosmo.fill(vvv,{par=self,id=id,mc=mc})
					end
					local r1,r2=_dal:MakeNode(self.__id.."/"..id, vvv,nil,nil,true)
					if r1 and mc~='' then r1.__raw_v._cmc=mc end
					return r1,r2
				end
			end
		end
	elseif _cd._any then
		_ct=_cd._any
		local tmplrep
		if type(_ct)=='table' then
			tmplrep=_ct.tmpl
			if not _ct._mc then _ct=_ct[1] end
		end
		if type(_ct) == 'string' then
			if tmplrep then
				_ct=cosmo.fill(_ct,{par=self,id=id,mc=mc})
			end
			return _dal:MakeNode(self.__id.."/"..id,_ct,nil,nil,true)
		else
			local vd = _mc_sub_data(_dal,_ct,mc or '')
			if vd then
				if tmplrep then
					vd=cosmo.fill(vd,{par=self,id=id,mc=mc})
				end
				local r1,r2=_dal:MakeNode(self.__id.."/"..id,vd,nil,nil,true)
				if r1 and mc~='' then r1.__raw_v._cmc=mc end
				return r1,r2
			end
		end
	end
	return nil
end

function Node:__do_inh_act()
	local l_dpl=self.__dpl
	local _rv = self.__raw_v
	if _rv._pto=='' then _rv._pto=nil end
	local prototype_id = _rv._pto or l_dpl._pto_id
	
	if prototype_id:byte(1)==96 then
		prototype_id=GetRelID(prototype_id:sub(2),self.__id,l_dpl._sb)
	end
	
	--if node id is the dpl default root id , do nothing for inh
	if self.__id == prototype_id then
		self.__pto_v={}
		self.__inh_v = self.__raw_v
		self.__act_v = {}
		--active root node
		local fields, err = conf_do(_rv._fds,l_dpl._sb,l_dpl._log)
		self.__act_v._fds = fields
		local dfields
		if _rv._dfs then
			dfields, err = conf_do(_rv._dfs,l_dpl._sb,l_dpl._log)
			self.__act_v._dfs = dfields
		end
		for field_name, rvv in pairs(_rv) do
			if field_name ~= '_fds' and field_name ~= '_dfs' then
				local field = fields[field_name] or dfskey(dfields,field_name)
				if field then
					local _tmp = field.activate
					if _tmp then
						local activator_fn = Activators[_tmp]
						self.__act_v[field_name] = activator_fn(rvv, l_dpl,self,field_name)
					end
				end
			end
		end
		return
	elseif isroot(self.__id,prototype_id) then
		--prototypeid can't be the child node
		error(self.__id .. " can't use the child node " .. tostring(prototype_id) .. " as pto")
	end

	-- Get the values for the prototype.
	local proto_node = l_dpl.dal:SureNode(prototype_id)
	assert(proto_node,"can't get the root prototype node " .. tostring(prototype_id))
	self.__pto_v = proto_node
	local proto_fields = proto_node:__get_inh_field('_fds')
	assert(proto_fields, "The prototype node must define fields")

	if _rv._fds == '' then _rv._fds=nil end
	local tmp_fields = inheritance_rules.concat(proto_fields,_rv._fds)
	-- first active fields
	local fields, err
	if tmp_fields == proto_fields then
		fields = proto_node._fds
		self.__inh_v=false
		self.__act_v=false
	else
		self.__inh_v={_fds=tmp_fields}
		fields, err = conf_do(tmp_fields,l_dpl._sb,l_dpl._log)
		assert(fields,err)
		self.__act_v={_fds=fields}
	end

	if _rv._dfs == '' then _rv._dfs=nil end
	if _rv._dfs then
		local proto_d_fields = proto_node:__get_inh_field('_dfs')
		local tmp_fields = inheritance_rules.concat(proto_d_fields,_rv._dfs)
		-- first active fields
		local dfields
		if tmp_fields == proto_d_fields or tmp_fields=='' then
			dfields = proto_node._dfs
		else
			local t=self.__inh_v or {}
			t._dfs=tmp_fields
			dfields, err = conf_do(tmp_fields,l_dpl._sb,l_dpl._log)
			assert(dfields,err)
			t=self.__act_v or {}
			t._dfs=dfields
		end
	end
	
	assert(fields, err and err.err)

	for field_name, rvv in pairs(_rv) do
		if field_name ~= '_fds' and field_name ~= '_dfs' then
			local field = fields[field_name] or dfskey(self._dfs,field_name)
			if field and not field.dyn then
				local _tmp = field.proto
				--TODO:if _tmp and rvv~='' then
				-- need set rawval nil
				if _tmp then
					local inheritance_fn = inheritance_rules[_tmp] or inheritance_rules.default
					if not self.__inh_v then self.__inh_v={} end
					self.__inh_v[field_name] = inheritance_fn(proto_node:__get_inh_field(field_name),rvv,l_dpl,self,field_name)
				end
				_tmp = field.activate
				if _tmp then
					local activator_fn = Activators[_tmp]
					--local value = self[field_name]
					local value = self:__get_inh_field(field_name,field)
					if not self.__act_v then self.__act_v={} end
					self.__act_v[field_name] = activator_fn(value, l_dpl,self,field_name)
				end
			end
		end
	end
end

function Node:__do_inh_act_kv(field,key,rvv)
	field=field or self:__get_field_define(k)
	if not field then return end
	if (field[1] or 1) < 1 then return self:__do_inh_act() end
	if self.__inh_v then self.__inh_v[key]=nil end
	if self.__act_v then self.__act_v[key]=nil end
	if field.dyn then return end
	local _tmp = field.proto
	local __get_inh_field=self.__pto_v.__get_inh_field
	if __get_inh_field and _tmp and rvv~='' then
		local inheritance_fn = inheritance_rules[_tmp] or inheritance_rules.default
		if not self.__inh_v then self.__inh_v={} end
		self.__inh_v[key] = inheritance_fn(self.__pto_v:__get_inh_field(key),rvv,self.__dpl,self,key)
	end
	_tmp = field.activate
	if _tmp then
		local activator_fn = Activators[_tmp]
		--local value = self[key]
		local value = self:__get_inh_field(key,field)
		if not self.__act_v then self.__act_v={} end
		self.__act_v[key] = activator_fn(value, self.__dpl,self,key)
	end
end

function Node:__get_child(key)
	return self.__dpl.dal:GetNode(self.__id .. '/' .. key)
end

function Node:__sure_child(key,mc,nocache)
	return self.__dpl.dal:SureNode(self.__id .. '/' .. key,mc,nocache)
end

function Node:__get_child_all(cs)
	assert(not cs)
	local cs=self.__dpl.dal:GetChildNodesFromDpl(self.__id)
	if self._cfd then
		for k,v in pairs(self._cfd) do
			if not cs[k] then
				cs[k]=self.__dpl.dal:SureNode(self.__id .. '/' .. k,type(v)=='table' and v.mc or nil)
			end
		end
	end
	return cs
end
function Node:__get_child_fix()
	local cs={}
	if self._cfd then
		for k,v in pairs(self._cfd) do
			if not cs[k] then
				cs[k]=self:__sure_child(k,type(v)=='table' and v.mc or nil)
			end
		end
	end
	return cs
end

function Node:__get_child_key_list()
	local lst={}
	local cs=self.__dpl.dal:GetChildIdsEx(self.__id)
	if cs then
		local lp=#self.__id+2
		for _,k in ipairs(cs) do
			local sid=string.sub(k,lp)
			lst[sid]=k
		end
	end
	if self._cfd then
		for k,v in pairs(self._cfd) do
			if not lst[k] then
				lst[k]=self:__sure_child(self.__id .. '/' .. k,type(v)=='table' and v.mc or nil)
			end
		end
	end
	local keys={}
	for k,v in pairs(lst) do
		table_insert(keys,k)
	end
	return keys
end

function Node:__get_child_ex(where,sort,limit)
	local cs=self.__dpl.dal:GetChildNodesEx(self.__id,where,sort,limit)
	if self._cfd then
		for k,v in pairs(self._cfd) do
			if not cs[k] then
				cs[k]=self.__dpl.dal:SureNode(self.__id .. '/' .. k,type(v)=='table' and v.mc or nil)
			end
		end
	end
	return cs
end

function Node:__remove_child(key,deep)
	self.__dpl.dal:__remove_ta(self.__id..'/'..key,deep)
end

function Node:__add_child(key,mc,s)
	if self:__get_child(key) then return nil, 'ESN1E:'..key end
	local _t,errmsg = s
	if not s then
		_t,errmsg=self:__make_child(key,mc)
		if not _t then return nil, errmsg end
	end
	_t:__save_ta()
	return _t
end
local function in_table(tab, item)
	for i,v in ipairs(tab) do
		if v==item then return true end
	end
	return false
end

-- checks membership in groups
local function member(item, group, node)
	local tg=type(group)
	if tg == "function" then return group(item, node)
	elseif tg == "table" then return in_table(group, item)
	elseif tg == "string" then return group == item
	else error("expected a string, a table or a function, but it is "..tg)
	end
end


function Node:__check_acl(user, action)
	if not self._acl then return true end

	-- keeps the allowed/not allowed state
	local has_permission = false
	-- toggles the the state
	local function set(user_group, action_group, value)
		if member(user, user_group, self) and member(action, action_group, self) then
			has_permission = value
		end
	end

	-- setup the sandbox
	local ok,msg=fast_do(setmetatable({
		allow=function (user, action) set(user, action, true) end,
		deny=function (user, action) set(user, action, false) end,
	},{__index=self.__dpl._secugroups}),self._acl)
	if not ok then
		error(msg)
	end
	return has_permission
end

function Node:__check_see(user, kkk)
	if not self._see then return true end
	return true;
end

function Node:__check_mdf(user, kkk)
	if not self._see then return true end
	return true;
end

function Node:__save()
	return self.__dpl:_save_node(self)
end

function Node:__remove(deep)
	self.__dpl.dal:RemoveNode(self.__id,deep)
end
function Node:__remove_subnodes()
	self.__dpl.dal:RemoveSubNodes(self.__id)
end

function Node:__save_ta()
	self.__dpl:_ta_save_node(self)
end
function Node:__remove_ta(deep)
	self.__dpl.dal:RemoveNodeTA(self.__id,deep)
end
function Node:__remove_subnodes_ta()
	self.__dpl.dal:RemoveSubNodesTA(self.__id)
end

function Node:__get_raw_data()
	return zdc.deflate.deflate_node(self.__raw_v, self._fds,self._dfs)
end

function Node:__show_raw_k(k,_fd)
	local _fd=_fd or self:__get_field_define(k)
	if not _fd then return nil end
	local v = self.__raw_v[k] or _fd.v
	local ft=_fd[2]
	if ft and ft._showS then
		return ft:_showS(v)
	else
		return tostring(v or '')
	end
end
function Node:__show_inh_k(k,_fd)
	local _fd=_fd or self:__get_field_define(k)
	if not _fd then return nil end
	local v = self:__get_inh_field(k,_fd)
	local ft=_fd[2]
	if ft and ft._showS then
		return ft:_showS(v)
	else
		return tostring(v or '')
	end
end
function Node:__show_val_k(k,_fd)
	local _fd=_fd or self:__get_field_define(k)
	if not _fd then return nil end
	local v = self[k]
	local ft=_fd[2]
	if ft and ft._showS then
		return ft:_showS(v)
	else
		return tostring(v or '')
	end
end
------------------------------
function Node:__get_doc_disp(l)
	return get_doc(self._doc,'disp',l) or self.__id
end
------------------------------
function Node:__tostring()
	local buf = "================= "..self.__id.." ========\n"
	for field, fieldinfo in pairs(self._fds) do
		buf = buf.."~~~~~~~ "..field.." ("..(fieldinfo.proto or "")..") ~~~~\n"
		buf = buf..(self.__raw_v[field] or "")
		buf = buf.."\n"
	end
	buf = buf.."~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n"
	return buf
end
